﻿#ifndef TEMPLATEMATCH_H
#define TEMPLATEMATCH_H

#include <vector>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>

namespace Qly {


[[deprecated]]
double templateMatch(const cv::Mat &image, const cv::Mat &tepl, const cv::Mat &mask, int method,
                                    cv::Point &point) noexcept; // 没有针对模板大小做归一化

bool templateMatch(const cv::Mat &image, const cv::Mat &tepl, const cv::Mat &mask, int method,
                   cv::Point &point, double &score) noexcept;
bool templateMatch(const cv::Mat &image, const cv::Mat &tepl, const cv::Mat &mask, int method,
                   cv::Rect &rect, double &score) noexcept;

/**
 * @brief templateMatchCCORR CCORR 准则的模板匹配，之所以单独拿出来，是因为这个实测最快
 * @param image 输入图像
 * @param tepl 模板图像
 * @param mask 掩膜图像，如果没有，可以填写 cv::Mat()
 * @param point 输出匹配点坐标
 * @param score 匹配度，越大越好
 * @return
 */
bool templateMatchCCORR(const cv::Mat &image, const cv::Mat &tepl, const cv::Mat &mask,
                        cv::Point &point, double &score) noexcept;
bool templateMatchCCORR(const cv::Mat &image, const cv::Mat &tep, const cv::Mat &maskl,
                        cv::Rect &rect, double &score) noexcept;

/**
 * @brief templateMatchCCORR_NORMED  CCORR_NORMED准则的模板匹配，之所以单独拿出来，是因为这个在归一化的匹配方法中实测最快
 * @param image 输入图像
 * @param tepl 模板图像
 * @param mask 掩膜图像，如果没有，可以填写 cv::Mat()
 * @param point 输出匹配点坐标
 * @param score 匹配度，1 表示最佳匹配，0表示不匹配，-1表示反向匹配
 * @return
 */
bool templateMatchCCORR_NORMED(const cv::Mat &image, const cv::Mat &tepl, const cv::Mat &mask,
                               cv::Point &point, double &score) noexcept;
bool templateMatchCCORR_NORMED(const cv::Mat &image, const cv::Mat &tepl, const cv::Mat &mask,
                               cv::Rect &rect, double &score) noexcept;

bool templateMatchCCOEFF_NORMED(const cv::Mat &image, const cv::Mat &model, const cv::Mat &mask,
                                cv::Point &point, double &score) noexcept;
bool templateMatchCCOEFF_NORMED(const cv::Mat &image, const cv::Mat &model, const cv::Mat &mask,
                                cv::Rect &rect, double &score) noexcept;


/**
 * @brief drawRotatedRect 在图像上画出旋转的矩形
 * @param [inout] image
 * @param [in] rrt 旋转的举行
 * @param color 颜色
 * @param thickness 线宽
 */
bool drawRotatedRect(cv::Mat &image, cv::RotatedRect &rrt,
                     cv::Scalar color = cv::Scalar(255, 255, 255), int thickness = 1);

/**
 * @brief The TemplateMatch class 这个类主要解决图像旋转匹配问题。 如果不考虑旋转那么 match 函数就够用了。
 *
 */
class TemplateMatch
{
public:
    enum class MODEL_SIZE_MODE {FIX_MODEL_SIZE, AUTO_MODEL_SIZE, FULL_MODEL_SIZE};

    /**
     * @brief setMethod 设置寻找最佳角度的方法
     * @param method
     */
    void setMethod( int method = cv::TM_CCORR ) {m_matchMethod = method;}
    void setSizeMode(MODEL_SIZE_MODE mode) {m_sizeMode = mode;}
    void setSubPixMode(bool enable) {m_enableSubPix = enable;}

    bool setModelByAngleCount(const cv::Mat &model, double angleMin, double angleMax, size_t angleCount);
    bool setModelByAngleCount(const cv::Mat &model, const cv::Mat &mask, double angleMin, double angleMax, size_t angleCount);

    bool setModelByDetAngle(const cv::Mat &model, double angleMin, double angleMax, double detAngle);
    bool setModelByDetAngle(const cv::Mat &model, const cv::Mat &mask, double angleMin, double angleMax, double detAngle);

    bool singleMatch(const cv::Mat &image, int angleIndex, cv::RotatedRect &rrt, double &score);
    bool singleMatch(const cv::Mat &image, int angleIndex, cv::Rect &rect, double &score);
    bool rotatedMatch(const cv::Mat &image, cv::RotatedRect &rrt, double &score);
    bool rotatedMatch(const cv::Mat &image, cv::Rect &rect, size_t &index, double &score);

    size_t angleCount() {return m_angleCount;}
    bool rectSize(size_t indexMin, size_t indexMax, int &width, int &height); //返回这个范围内的 model 的尺寸

    bool getModel(size_t angleIndex, cv::Mat &model);
protected:
    bool better(double oldGrade, double newGrade, int method);
    bool angleInterpolate(size_t index, double &angle, double& score);

    bool buildModel(const cv::Mat &model, const cv::Mat &mask);
    cv::Mat m_model;
    cv::Mat m_mask;
    cv::Size m_modelSize;
    std::vector<cv::Mat> m_rotatedModels;
    std::vector<cv::Mat> m_rotatedMasks;
    std::vector<double> m_angles;
    std::vector<double> m_scores;

    double m_angleMin;
    double m_angleMax;
    double m_angleDet;
    size_t m_angleCount;

    MODEL_SIZE_MODE m_sizeMode = MODEL_SIZE_MODE::FIX_MODEL_SIZE;
    int m_matchMethod = cv::TM_CCORR_NORMED;
    bool m_enableSubPix = false;
};
}
#endif // TEMPLATEMATCH_H
